package com.mall.common.service.impl;

import static com.mall.common.bean.CommonAttributes.UPLOAD_TYPE_FILE;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Repository;
import org.springframework.web.multipart.MultipartFile;

import com.mall.common.CommonSetting;
import com.mall.common.bean.FileInfo;
import com.mall.common.bean.FileInfo.FileType;
import com.mall.common.bean.FileInfo.OrderType;
import com.mall.common.enums.UploadType;
import com.mall.common.image.ImageUtil;
import com.mall.common.plugin.storage.StoragePlugin;
import com.mall.common.plugin.storage.StoragePluginBundle;
import com.mall.common.service.FileService;
import com.mall.common.util.AppContext;
import com.mall.common.util.FreemarkerUtils;

@Repository
public class FileServiceImpl implements FileService {

	@Resource
	private TaskExecutor taskExecutor;
	@Resource
	private StoragePluginBundle storagePluginBundle;

	// 异步上传文件
	private void asyncUpload(File file, String destFilePath,
			String contentType, String componentId) {
		StoragePlugin storageplugin = storagePluginBundle.getPluginMap().get(
				componentId);
		taskExecutor.execute(new UploadRunnable(file, destFilePath,
				contentType, storageplugin));
	}

	// 根据系统设置的文件存储路径模板获取路径(包含生成的文件名)
	private String getPathByFileType(FileType fileType,
			MultipartFile multipartFile, UploadType uploadType,
			Map<String, Object> extra, CommonSetting setting) {
		String path;
		String prefix = "";
		if (fileType == FileType.flash) {
			path = setting.getFlashUploadPath();
		} else if (fileType == FileType.media) {
			path = setting.getMediaUploadPath();
		} else if (fileType == FileType.file) {
			path = setting.getFileUploadPath();
		} else {
			path = setting.getImageUploadPath();
		}
		Map<String, Object> hashmap = new HashMap<String, Object>();
		hashmap.put("uuid", UUID.randomUUID().toString());
		hashmap.put("uploadType", uploadType);
		if (extra != null) {
			hashmap.putAll(extra);
			if (extra.get("prefix") != null) {
				prefix = extra.get("prefix").toString();
			}
		}
		// 文件路径名
		String pathPrefix = FreemarkerUtils.process(path, hashmap);
		return new StringBuilder(pathPrefix)
				.append(prefix)
				.append(UUID.randomUUID())
				.append(".")
				.append(FilenameUtils.getExtension(multipartFile
						.getOriginalFilename())).toString();
	}

	public boolean isValid(FileType fileType, MultipartFile multipartFile, CommonSetting setting) {
		if (multipartFile == null) {
			return false;
		}
		if (setting.getUploadLimit() != null
				&& setting.getUploadLimit() != 0
				&& multipartFile.getSize() > (long) setting.getUploadLimit()
						.intValue() * 1024L * 1024L) {
			return false;
		}
		String extensions[];
		if (fileType == FileType.flash) {
			extensions = setting.getUploadFlashExtensions();
		} else if (fileType == FileType.media) {
			extensions = setting.getUploadMediaExtensions();
		} else if (fileType == FileType.file) {
			extensions = setting.getUploadFileExtensions();
		} else {
			extensions = setting.getUploadImageExtensions();
		}
		if (ArrayUtils.isNotEmpty(extensions)) {
			return FilenameUtils.isExtension(
					multipartFile.getOriginalFilename().toLowerCase(), extensions);
		} else {
			return false;
		}
	}

	public String upload(File uploadFile, String contentType, String filePath,
			boolean async, String componentSn) {
		if (uploadFile == null || componentSn == null) {
			return null;
		}
		if (async) {
			asyncUpload(uploadFile, filePath, contentType, componentSn);
			return null;
		} else {
			StoragePlugin storageplugin = storagePluginBundle.getPluginMap()
					.get(componentSn);
			storageplugin.upload(filePath, uploadFile, contentType);
			return storageplugin.getUrl(filePath);
		}
	}

	public String upload(FileType fileType, MultipartFile multipartFile,
			boolean async, String componentSn, UploadType uploadType,
			Map<String, Object> extra, CommonSetting setting) {
		if (multipartFile == null || componentSn == null) {
			return null;
		}
		// 系统根据文件类型生成的文件路径和文件名
		String destFilePath = getPathByFileType(fileType, multipartFile,
				uploadType, extra, setting);
		File tempFile = new File(System.getProperty("java.io.tmpdir")
				+ "/upload_" + UUID.randomUUID() + ".tmp");
		try {
			multipartFile.transferTo(tempFile);
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (async) {
			asyncUpload(tempFile, destFilePath, multipartFile.getContentType(),
					componentSn);
			return null;
		} else {
			StoragePlugin storageplugin = storagePluginBundle.getPluginMap()
					.get(componentSn);
			storageplugin.upload(destFilePath, tempFile,
					multipartFile.getContentType());
			return storageplugin.getUrl(destFilePath);
		}
	}

	public List<FileInfo> browser(UploadType uploadType, String path,
			FileType fileType, OrderType orderType, String componentId, CommonSetting setting) {
		if (path != null) {
			if (!path.startsWith("/"))
				path = new StringBuilder("/").append(path).toString();
			if (!path.endsWith("/"))
				path = new StringBuilder(path).append("/").toString();
		} else {
			path = "/";
		}
		String path2 = null;
		if (fileType == FileType.flash) {
			path2 = setting.getFlashUploadPath();
		} else if (fileType == FileType.media) {
			path2 = setting.getMediaUploadPath();
		} else if (fileType == FileType.file) {
			path2 = setting.getFileUploadPath();
		} else {
			path2 = setting.getImageUploadPath();
		}

		Map<String, Object> hashmap = new HashMap<String, Object>();
		hashmap.put("uploadType", uploadType);
		
		// 文件路径名
		String s1 = FreemarkerUtils.process(path2, hashmap);
		s1 = StringUtils.substringBeforeLast(s1, "/");
		s1 = StringUtils.substringBeforeLast(s1, "/");
		if(StringUtils.isNotBlank(path)){
			s1 += path;
		}

		List<FileInfo> fileList = new ArrayList<FileInfo>();
		if (s1.indexOf("..") >= 0) {
			return fileList;
		}

		StoragePlugin storageplugin = storagePluginBundle.getPluginMap().get(
				componentId);
		fileList.addAll(storageplugin.browser(s1));

		if (orderType == OrderType.size) {
			Collections.sort(fileList, new SizeComparator());
		} else if (orderType == OrderType.type) {
			Collections.sort(fileList, new TypeComparator());
		} else if (orderType == OrderType.date) {
			Collections.sort(fileList, new DateComparator());
		} else {
			Collections.sort(fileList, new NameComparator());
		}
		return fileList;
	}

	// 文件多线程上传
	private class UploadRunnable implements Runnable {

		private File file;
		private StoragePlugin storagePlugin;
		private String filePath; // 文件路径
		private String contentType;// 文件类型(image/jpeg, image/png)

		public void run() {
			storagePlugin.upload(filePath, file, contentType);
		}

		public UploadRunnable(File file, String filePath, String contentType,
				StoragePlugin storagePlugin) {
			this.file = file;
			this.storagePlugin = storagePlugin;
			this.filePath = filePath;
			this.contentType = contentType;
		}
	}

	class SizeComparator implements Comparator<FileInfo> {

		@Override
		public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
			return new CompareToBuilder()
					.append(!fileInfos1.getIsDirectory(),
							!fileInfos2.getIsDirectory())
					.append(fileInfos1.getSize(), fileInfos2.getSize())
					.toComparison();
		}

	}

	class DateComparator implements Comparator<FileInfo> {

		@Override
		public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
			return new CompareToBuilder()
					.append(!fileInfos1.getIsDirectory(),
							!fileInfos2.getIsDirectory())
					.append(fileInfos2.getLastModified(),
							fileInfos1.getLastModified()).toComparison();
		}

	}

	class TypeComparator implements Comparator<FileInfo> {

		@Override
		public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
			return new CompareToBuilder()
					.append(!fileInfos1.getIsDirectory(),
							!fileInfos2.getIsDirectory())
					.append(FilenameUtils.getExtension(fileInfos1.getName()),
							FilenameUtils.getExtension(fileInfos2.getName()))
					.toComparison();
		}
	}

	class NameComparator implements Comparator<FileInfo> {
		@Override
		public int compare(FileInfo fileInfos1, FileInfo fileInfos2) {
			return new CompareToBuilder()
					.append(!fileInfos1.getIsDirectory(),
							!fileInfos2.getIsDirectory())
					.append(fileInfos1.getName(), fileInfos2.getName())
					.toComparison();
		}
	}

	@Override
	public String uploadTemp(FileType fileType, MultipartFile multipartFile,
			boolean async, Map<String, Object> extra, CommonSetting setting) {
		return upload(fileType, multipartFile, async, UPLOAD_TYPE_FILE,
				UploadType.temp, extra, setting);
	}

	/**
	 * 获取文件本地路径
	 * 
	 * @param path
	 *            文件带组件名的路径
	 * @return
	 */
	private String replaceLocalPath(String path) {
		if (StringUtils.isEmpty(path)) {
			return null;
		}

		if (path.startsWith("http://")) {
			return null;
		}
		// 通过正则表达式获取组件的标识和ID
		String pattern = "(.*)" + "://" + "(.*)";
		Pattern p = Pattern.compile(pattern, 2 | Pattern.DOTALL);
		Matcher m = p.matcher(path);
		String file_path = null;
		if (m.find()) {
			file_path = m.replaceAll("$2");
			String pluginName = m.replaceAll("$1");
			StoragePlugin storagePlugin = storagePluginBundle.getPluginMap().get(pluginName);
			return storagePlugin.getPath(file_path);
		} else {
			return null;
		}
	}

	@Override
	public String moveFile(FileType fileType, String path,
			UploadType uploadType, String componentId, Map<String, Object> extra, CommonSetting setting) {
		if (StringUtils.isBlank(path) || componentId == null) {
			return null;
		}
		String pathTemplate;
		if (fileType == FileType.flash) {
			pathTemplate = setting.getFlashUploadPath();
		} else if (fileType == FileType.media) {
			pathTemplate = setting.getMediaUploadPath();
		} else if (fileType == FileType.file) {
			pathTemplate = setting.getFileUploadPath();
		} else {
			pathTemplate = setting.getImageUploadPath();
		}
		String fileName = null;
		Map<String, Object> hashmap = new HashMap<String, Object>();
		hashmap.put("uuid", UUID.randomUUID().toString());
		hashmap.put("uploadType", uploadType);
		if (extra != null) {
			hashmap.putAll(extra);
			if (hashmap.get("fileName") != null) {
				fileName = hashmap.get("fileName").toString();
			}
		}
		if (StringUtils.isBlank(fileName)) {
			fileName = StringUtils.substringAfterLast(path, "/");
		} else {
			fileName += ".jpg";
		}
		// 文件路径名
		String pathPrefix = FreemarkerUtils.process(pathTemplate, hashmap);
		String destFilePath = pathPrefix + fileName;
		path = replaceLocalPath(path);
		if (StringUtils.isBlank(path)) {
			return null;
		}
		StoragePlugin storageplugin = storagePluginBundle.getPluginMap().get(
				componentId);
		storageplugin.moveLocal(destFilePath, new File(path));
		return storageplugin.getUrl(destFilePath);
	}

	@Override
	public String cutImage(String path, Integer boxWidth, Integer boxHeight, Integer axisX, Integer axisY,
			Integer height, Integer width, UploadType uploadType,
			Map<String, Object> extra, CommonSetting setting) {
		String pathTemplate = setting.getImageUploadPath();
		String fileName = null;
		Map<String, Object> hashmap = new HashMap<String, Object>();
		hashmap.put("uuid", UUID.randomUUID().toString());
		hashmap.put("uploadType", uploadType);
		if (extra != null) {
			hashmap.putAll(extra);
			if (hashmap.get("fileName") != null) {
				fileName = hashmap.get("fileName").toString();
			}
		}
		if (StringUtils.isBlank(fileName)) {
			fileName = StringUtils.substringAfterLast(path, "/");
		} else {
			fileName += ".jpg";
		}
		String pathPrefix = FreemarkerUtils.process(pathTemplate, hashmap);
		String destFilePath = pathPrefix + fileName;
		
		String pattern = "(.*)" + "://" + "(.*)";
		Pattern p = Pattern.compile(pattern, 2 | Pattern.DOTALL);
		Matcher m = p.matcher(path);
		StoragePlugin storagePlugin = null;
		if (m.find()) {
			String pluginName = m.replaceAll("$1");
			storagePlugin = storagePluginBundle.getPluginMap().get(pluginName);
			destFilePath = storagePlugin.getPath(destFilePath);
		}else{
			destFilePath = AppContext.getAppRealPath(destFilePath);
		}
		
		path = replaceLocalPath(path);
		if (StringUtils.isBlank(path)) {
			return null;
		}
		
		if(ImageUtil.crop(new File(path), new File(destFilePath), axisX, axisY, width, height)){
			if(storagePlugin != null){
				return storagePlugin.getRelativePath(destFilePath);
			}
			return destFilePath;
		}
		return null;
	}

	@Override
	public String copyFile(FileType fileType, String path,
			UploadType uploadType, String componentId, Map<String, Object> extra, CommonSetting setting) {
		if (StringUtils.isBlank(path) || componentId == null) {
			return null;
		}
		String pathTemplate;
		if (fileType == FileType.flash) {
			pathTemplate = setting.getFlashUploadPath();
		} else if (fileType == FileType.media) {
			pathTemplate = setting.getMediaUploadPath();
		} else if (fileType == FileType.file) {
			pathTemplate = setting.getFileUploadPath();
		} else {
			pathTemplate = setting.getImageUploadPath();
		}
		String fileName = null;
		Map<String, Object> hashmap = new HashMap<String, Object>();
		hashmap.put("uuid", UUID.randomUUID().toString());
		hashmap.put("uploadType", uploadType);
		if (extra != null) {
			hashmap.putAll(extra);
			if (hashmap.get("fileName") != null) {
				fileName = hashmap.get("fileName").toString();
			}
		}
		if (StringUtils.isBlank(fileName)) {
			fileName = StringUtils.substringAfterLast(path, "/");
		} else {
			fileName += ".jpg";
		}
		// 文件路径名
		String pathPrefix = FreemarkerUtils.process(pathTemplate, hashmap);
		String destFilePath = pathPrefix + fileName;
		path = replaceLocalPath(path);
		if (StringUtils.isBlank(path)) {
			return null;
		}
		StoragePlugin storageplugin = storagePluginBundle.getPluginMap().get(
				componentId);
		storageplugin.copyLocal(destFilePath, new File(path));
		return storageplugin.getUrl(destFilePath);
	}

}
